home *** CD-ROM | disk | FTP | other *** search
/ Amiga Developer CD 2.1 / Amiga Developer CD v2.1.iso / Reference / Amiga_Mail_Vol1 / IFF / SMUS < prev   
Encoding:
Text File  |  1999-10-27  |  36.4 KB  |  1,113 lines

  1. (c)  Copyright 1989-1999 Amiga, Inc.   All rights reserved.
  2. The information contained herein is subject to change without notice, and 
  3. is provided "as is" without warranty of any kind, either expressed or implied.  
  4. The entire risk as to the use of this information is assumed by the user.
  5.  
  6.  
  7.  
  8.  
  9.  
  10.  
  11.                             A SMUS Player
  12.                             
  13.                              by Dan Baker
  14.  
  15.  
  16.  
  17.  
  18.      SMUS is the name of the IFF standard which provides for the storage of
  19. musical scores.  Most score editors, such as Deluxe Music, allow the user
  20. to store and read music data in the SMUS format.  By supporting  SMUS,
  21. applications from different vendors can share music files directly without
  22. special handling.
  23.  
  24.      SMUS files contain note and duration information.  A second IFF file
  25. type, called 8SVX, contains information on the instruments for a score.
  26. 8SVX files contain an 8-bit sample for an instrument, usually at several
  27. octaves, and some play back information.  Together, SMUS and 8SVX are
  28. capable of representing most audio information.  This article presents
  29. brief overview of the SMUS format and a program which will play back
  30. SMUS scores on the Amiga.
  31.  
  32.  
  33. SMUS Header Chunk
  34.  
  35. There are many chunk types in the SMUS form but only 3 need to be
  36. considered for play back: SHDR, INS1 and TRAK.  SHDR is the score header
  37. chunk and has the following structure:
  38.  
  39.      typedef struct { UWORD tempo;     /* tempo, 128th quarter notes/min */
  40.                       UBYTE volume;    /* overall volume                 */
  41.                       UBYTE ctTrack;   /* number of tracks               */
  42.                     } SScoreHeader;
  43.  
  44. SHDR gives the overall tempo and volume.  It also tells how many seperate
  45. tracks are contained in the file.  Typically, there is a track for each
  46. channel or voice of the host computer.  For instance, Amiga SMUS files
  47. often have four tracks - one for each Amiga audio channel.
  48.  
  49.  
  50.  
  51.  
  52. SMUS Instrument Chunk
  53.  
  54.      For most SMUS files, the SHDR chunk is followed by one or more INS1
  55. chunks.  The INS1 chunks identify instruments used in the score and have
  56. the following structure:
  57.  
  58.      typedef struct { UBYTE register;     /* instrument register #  */
  59.                       UBYTE type;         /* type: 0=named 1=midi # */
  60.                       UBYTE data1,data2;  /* midi numbers           */
  61.                       CHAR  name[];       /* instrument name        */
  62.                     } RefInstrument;
  63.  
  64. INS1 chunks allow a playback program to load and set up instrument samples
  65. from 8SVX files ahead of time.  The instrument samples are used each time a
  66. note is played on the Amiga's audio hardware.  Note that INS1 chunks may be
  67. ignored and a default instrument sample used instead.
  68.  
  69.  
  70.  
  71. SMUS TRAK Chunk
  72.  
  73. Instrument chunks are usually followed by 4 TRAK chunks.  Each TRAK
  74. chunk consists of a stream of SEvents (score events) which describe the
  75. frequency and duration of each note.  An SEvent is a 2-byte structure:
  76.  
  77.                                       /*  0-127 = note, 128=rest,    */
  78.      typedef struct { UBYTE sID;      /*  > 128 = other SEvent type  */
  79.                       UBYTE data;     /*  Note or rest duration      */
  80.                     } SEvent;
  81.  
  82.      An SEvent sID value in the range 0-127 represents the corresponding
  83. midi note number.  Middle C has a sID of 60.  Concert A has a sID of 69.
  84. An SEvent sID of 128 indicates a rest.  An SEvent sID of greater than 128
  85. indicates a TRAK command such as change instruments, change volume or 
  86. change tempo.
  87.  
  88. The duration of the note or rest in an SEvent is given by the data field.  
  89. Calcualting a duration from the data field is not straight forward - the
  90. data byte is sub-divided into five bit-fields.  The bits are assigned as 
  91. follows:
  92.  
  93.  
  94.     MSB                                                     LSB
  95.  
  96.     +---+      +---+     +---+---+       +---+     +---+---+---+
  97.     | 7 |      | 6 |     | 5 | 4 |       | 3 |     | 2 | 1 | 0 |
  98.     +---+      +---+     +---+---+       +---+     +---+---+---+
  99.     0=no      0=no tie  0 = no n-tuple   0=not       0=whole note
  100.      chord    1=tied    1 = 2/3            dotted    1=half note
  101.     1=chord             2 = 4/5          1=dotted    2=qrtr note
  102.                         3 = 6/7                           :
  103.                                                      7=128th note
  104.  
  105. To compute the duration you use:
  106.  
  107.           {1, 2/3, 4/5, 6/7}  *  {1, 3/2}  *  2 ! -{0, 1, 2...7}
  108.  
  109. where the numbers in brackets are indexed by the lower six bits.  To see
  110. how this works, consider an example.  An SEvent data byte of 18 would be 
  111. "0 0 01 0 010" which gives a duration of  {2/3} * {1} * {2 ! -2}.  That is
  112. the 2/3 n-tuple, not dotted, times a quarter note.
  113.  
  114. The chord bit and tie bit do not affect the calculated duration.  Instead
  115. they give information on how to handle the note.  The chord bit is typically 
  116. not used.  Chords are normally recorded as simultaneous notes on different 
  117. tracks not the same track.  The tie bit indicates that the current note 
  118. should continue to play for the given duration instead of starting a new note.
  119.  
  120. The TRAK chunks are the main part of a SMUS file giving the tone and
  121. duration for each note in the score.  The SMUS player spends most of its time
  122. decoding TRAK chunk SEvents and playing them for the duration indicated.
  123.  
  124.  
  125.  
  126.  
  127. The SMUS Player
  128.  
  129. The program shown below will play an IFF SMUS file on the Amiga's audio
  130. hardware from the CLI.  To use the program, type "sread smusfile" at the 
  131. CLI prompt.
  132.  
  133. The SMUS player uses the 8SVX instruments specified by the file.  The 8SVX
  134. file is decoded into several sets of samples, an attack or one-shot part
  135. and a sustain or repeating part at each of 8 octaves.  If the 8SVX file
  136. has less than 8 octaves the missing ones are "filled in" by using the 
  137. closest octave available.  A period table for each instrument gives the
  138. playback sampling speeds to use for each of the 12 tones in an octave.
  139.  
  140. The SMUS player does not handle every aspect of the IFF specification. 
  141. For instance, the ADSR chunks of an 8SVX voice files are skipped. Likewise,
  142. tied notes and 1-TRAK chords are not supported.  The tied notes will be
  143. played separately and only the first note of 1-TRAK chords will be played.
  144. A default volume of 60 is used for all notes.  SEvents which give TRAK 
  145. commands such as change instrument, change volume or change tempo are also
  146. skipped.  
  147.  
  148. Each TRAK is decoded and played back in real-time without syncing them
  149. to the system clock.  Two AudioIOBs are kept going for each channel so the
  150. next note always begins as soon as the last one is done.  However since the 
  151. SMUS player uses the timer device, there may be delays on a heavily loaded 
  152. system.  In that case adjust the priority of the AudioIOBs and the task.  
  153. The player works well with DMCS SMUS files.  However, scores that use Sonix
  154. instrument files will not work since Sonix instruments use a proprietary 
  155. format - they are not IFF.
  156.  
  157. SMUS and 8SVX are the only IFF standards that deal with audio.  These two 
  158. file standards are described in detail in the IFF documentation and disk
  159. available through CATS.  To order, send a check for $20 made out to
  160. Commodore to:
  161.                   CATS Orders
  162.                   1200 Wilson Dr.
  163.                   West Chester, PA 19380
  164.  
  165. Indidcate on the check or letter "IFF".  Allow 2-6 weeks for processing.
  166.  
  167.  
  168. ----------------------CODE STARTS HERE----------------------
  169. #include "exec/types.h"
  170. #include "exec/memory.h"
  171. #include "libraries/dosextens.h"
  172. #include "devices/audio.h"
  173. #include "iff/iff.h"
  174. #include "iff/smus.h"
  175.  
  176. #define SMUS MakeID('S','M','U','S')
  177. #define SHDR MakeID('S','H','D','R')
  178. #define INS1 MakeID('I','N','S','1')
  179. #define TRAK MakeID('T','R','A','K')
  180.  
  181.  
  182. struct FileHandle *Open();
  183. extern APTR        AllocMem();
  184. extern void        notes();      /* Plays the SEvents  */
  185. extern void        killaudio();  /* Finish for notes() */
  186. extern ULONG       setaudio();   /* Set up for notes() */
  187. extern ULONG       get8();       /* Gets 8SVX sample   */
  188.        void        kill();       /* Finish for main()  */
  189.        LONG        keepout();    /* Break handling     */
  190.  
  191. /*---------------*/
  192. /* G L O B A L S */
  193. /*---------------*/
  194. extern ULONG       ttable[];      /* To scale tempo     */
  195. extern LONG        scount;        /* No. of samples     */
  196. extern LONG        instrumentno;  /* No. of instruments */
  197.  
  198. struct FileHandle  *playhandle;      /* File handle for SMUS            */
  199.        UBYTE       *psdata;          /* Pointer to start of SMUS data   */
  200.        ChunkHeader *pChunkHeader;    /* Pointer to SMUS Header data     */
  201.        SEvent      *ptrak[8];        /* 1st and last events for 4 traks */
  202.        LONG         trakcount;       /* Number of traks */
  203.  
  204. /*-----------*/
  205. /*  M A I N  */
  206. /*-----------*/
  207. LONG
  208. main(argc,argv)
  209. LONG argc;
  210. char *argv[];
  211.  
  212. {
  213. /*-------------*/
  214. /* L O C A L S */
  215. /*-------------*/
  216. SEvent            *pSEvent;            /* Pointers for SMUS parts,    */
  217. RefInstrument     *pRefInstrument;     /* buffers for instrument name */
  218. SScoreHeader      *pSScoreHeader;      /* and FORM ID and counters.   */
  219. Chunk             *pChunk;
  220. char              *playname,inname[60],iobuffer[8];
  221. LONG               x,rdcount,datacount,tscale;
  222. ULONG              stat;
  223. /*-------------*/
  224. /*   C O D E   */
  225. /*-------------*/
  226. onbreak(&keepout);                     /* Lattice 4.0 Break Handling */
  227.  
  228. /*-----------------*/
  229. /* Check Arguments */
  230. /*-----------------*/
  231. playhandle=0;scount=0;instrumentno=0;stat=1;
  232. if(argv[1]==NULL) kill("No file name given.\n");
  233.  
  234. /*---------------*/
  235. /* Open the File */
  236. /*---------------*/
  237. playname=argv[1];
  238. playhandle=Open(playname,MODE_OLDFILE);
  239. if(playhandle==0)  kill("Cannot open file.\n");
  240.  
  241. /*---------------*/
  242. /* Read the File */
  243. /*---------------*/
  244. rdcount=Read(playhandle,iobuffer,8);
  245. if(rdcount==-1) kill ("Bad file during read.\n");
  246. if(rdcount<8)   kill ("Not an IFF file, too short.\n");
  247.  
  248. /*-----------------*/
  249. /* Evaluate Header */
  250. /*-----------------*/
  251. pChunkHeader=(ChunkHeader *)iobuffer;
  252. if( pChunkHeader->ckID != FORM ) kill("Cannot handle this IFF.\n");
  253. if(pChunkHeader->ckSize > 65536) kill("SMUS file too big.\n");
  254.  
  255. /*------------------*/
  256. /* Get the IFF data */
  257. /*------------------*/
  258. psdata=(UBYTE *) AllocMem(pChunkHeader->ckSize , MEMF_PUBLIC | MEMF_CLEAR);
  259. if(psdata==0)    kill("Memeory allocate failed.\n");
  260.  
  261. rdcount=Read(playhandle,psdata,pChunkHeader->ckSize);
  262. if(rdcount==-1)
  263.    kill ("Choked on file during read.\n");
  264. if(rdcount<pChunkHeader->ckSize)
  265.    kill ("Malformed IFF, too short.\n");
  266.  
  267. /*-------------------*/
  268. /* Evaluate IFF Type */
  269. /*-------------------*/
  270. if(MakeID( *psdata, *(psdata+1) , *(psdata+2) , *(psdata+3) ) != SMUS )
  271.   kill("Sorry, this is not IFF SMUS.\n");
  272.  
  273. /*--------------*/
  274. /* Find the IDs */
  275. /*--------------*/
  276. datacount=4;
  277. trakcount=0;
  278. while( datacount < pChunkHeader->ckSize)
  279.   {
  280.  
  281.   pChunk=(Chunk *)(psdata+datacount);
  282.   switch(pChunk->ckID)
  283.     {
  284.     case SHDR:
  285.       /*----------------------*/
  286.       /* Process Score Header */
  287.       /*----------------------*/
  288.       pSScoreHeader=(SScoreHeader *)(psdata+datacount+8L);
  289.       tscale=( 8929 / ( pSScoreHeader->tempo >> 7) );
  290.       for(x=0;x<64;x++)ttable[x]*=(ULONG)tscale;
  291.       break;
  292.     case INS1:
  293.       /*------------------------------------*/
  294.       /* Call get8() to Process Instrument  */
  295.       /*------------------------------------*/
  296.       pRefInstrument=(RefInstrument *)(psdata+datacount+8L);
  297.       for(x=0;x<=pChunk->ckSize;x++) inname[x] = pRefInstrument->name[x];
  298.       inname[pChunk->ckSize-4L]=0;
  299.       stat=get8(inname);
  300.       if(stat!=0)kill("Instrument problem\n");
  301.       break;
  302.     case TRAK:
  303.       /*------------------------------------*/
  304.       /* Set up pointers for TRAK handling  */
  305.       /*------------------------------------*/
  306.       if(trakcount<4)
  307.         {
  308.         pSEvent=(SEvent *)(psdata+datacount+8L);
  309.         ptrak[trakcount  ]=pSEvent;
  310.         ptrak[trakcount+4]=(SEvent *)(psdata+datacount+8L+pChunk->ckSize);
  311.         trakcount++;
  312.         }
  313.       else puts("More than 4 tracks. Skipping...\n");
  314.       pSEvent = (SEvent *)(psdata+datacount+8L+pChunk->ckSize);
  315.       break;
  316.  
  317.     default:
  318.       break;
  319.     }
  320.     /* end switch */
  321.  
  322.   datacount += 8L + pChunk->ckSize;        /* Go back for more Chunks */
  323.   if(pChunk->ckSize&1L ==1) datacount++;
  324.   }
  325.  
  326. /*-------------------------------*/
  327. /*  Play SMUS File with notes()  */
  328. /*-------------------------------*/
  329. if(stat==0 && instrumentno != 0) /* Must have one 8svx instrument */
  330.   {
  331.   stat=setaudio();
  332.  
  333.   if(stat==0)notes(ptrak);
  334.  
  335.   /*---------------*/
  336.   /*   Finish it   */
  337.   /*---------------*/
  338.   killaudio();
  339.   kill8svx();
  340.   }
  341.  
  342. /*-----------------------*/
  343. /* Normal end of program */
  344. /*-----------------------*/
  345. if(playhandle!=0L)  Close(playhandle);
  346. if(psdata    !=0L)  FreeMem(psdata,pChunkHeader->ckSize);
  347. return(0L);
  348.  
  349. }
  350.  
  351.  
  352.  
  353. /*----------------------*/
  354. /* Kill: Abort the Read */
  355. /*----------------------*/
  356. void
  357. kill(killstring)
  358. char *killstring;
  359. {
  360.    puts(killstring);
  361.    if(playhandle!=0L)Close(playhandle);
  362.    if(psdata !=0L)   FreeMem(psdata,pChunkHeader->ckSize);
  363.    exit(0L);
  364. }
  365.  
  366. /*-------------------------*/
  367. /*    For Break Handling   */
  368. /*-------------------------*/
  369. LONG
  370. keepout()
  371. {                      /* Pointer to keepout() used in onbreak().  */
  372. return (0);            /* This disables Lattice 4.0 break handling */
  373. }
  374.  
  375.  
  376.  
  377.  
  378. -------------------------MORE CODE STARTS HERE---------------------
  379. #include "exec/types.h"
  380. #include "exec/memory.h"
  381. #include "graphics/gfxbase.h"
  382. #include "libraries/dosextens.h"
  383. #include "iff/iff.h"
  384. #include "iff/8svx.h"
  385.  
  386. #define I8SVX MakeID('8','S','V','X')
  387. #define VHDR  MakeID('V','H','D','R')
  388. #define BODY  MakeID('B','O','D','Y')
  389.  
  390. #define INLIMIT 8               /* 4 Instrument limit (!)          */
  391.  
  392. struct GfxBase      *OpenLibrary();
  393. struct GfxBase      *GfxBase;
  394.        APTR          AllocMem();
  395. struct FileHandle   *Open();
  396. struct FileHandle   *inshandle;
  397.  
  398.        ULONG         get8();     /* Process instruments */
  399.        void          kill8svx(); /* Finish up get8()    */
  400.  
  401. /*---------------*/
  402. /* G L O B A L S */
  403. /*---------------*/
  404.       LONG         data8count,clock;        /* Counter and clock constant */
  405.       UBYTE        *p8data;                 /* Pointers to 8SVX data      */
  406.       Voice8Header *pVoice8Header;
  407.       Chunk        *p8Chunk;
  408.       LONG          fsize,instrumentno,scount;
  409.       UBYTE        *sbase,*sbases[INLIMIT];   /* Pointers to sample block */
  410.       LONG         *ptabptr,                  /* and calculated periods   */
  411.                    *ptabptrs[INLIMIT],        /* for 12 tones.  Table of  */
  412.                     ssize,ssizes[INLIMIT],    /* sample sizes for FreeMem */
  413.                     length[16*INLIMIT];    /* Lengths and pointers to the */
  414.       BYTE         *psample[16*INLIMIT];   /* attack and main parts of 8  */
  415.                                            /* octaves for each instrument */
  416.  
  417.                   /* Table used to calculate periods for 12 tones.        */
  418. LONG maxfreq[] =  { 41860, 44540, 46986, 49780, 52740, 55860,
  419.                     59200, 62720, 66448, 70400, 74586, 79022 }; /* C to B */
  420. /*-----------*/
  421. /*  G E T 8  */
  422. /*-----------*/
  423. ULONG
  424. get8(fname)
  425. char *fname;
  426.  
  427. {
  428. /*-------------*/
  429. /* L O C A L S */
  430. /*-------------*/
  431. char              iobuffer[8];
  432. LONG              rd8count,ifreq,freq,
  433.                   x,y,midin,ocyc,both,hic;
  434. UBYTE             *tempptr;
  435.  
  436. /*-------------*/
  437. /*   C O D E   */
  438. /*-------------*/
  439.  
  440. /*-----------------*/
  441. /* Check Arguments */
  442. /*-----------------*/
  443. if  (instrumentno>=INLIMIT) return(0L);
  444.  
  445. if(fname==NULL) {puts("No file name given.\n");
  446.                  return(1L);}
  447. /*-----------------------------------*/
  448. /* Initialize and Set Clock Constant */
  449. /*-----------------------------------*/
  450. if(instrumentno==0L)
  451.   {
  452.    for(x=0;x<INLIMIT;x++){ptabptrs[x]=0L;sbases[x]=0L;}
  453.  
  454.    GfxBase=(struct GfxBase *)OpenLibrary("graphics.library",0L);
  455.    if(GfxBase==0L){puts("Can't open graphics library\n");return(0L);}
  456.    if(GfxBase->DisplayFlags & PAL) clock=35468950L;      /* PAL clock */
  457.    else                            clock=35795450L;      /* NTSC clock */
  458.    if(GfxBase)CloseLibrary(GfxBase);
  459.   }
  460.  
  461. inshandle=0;p8data=0;data8count=0;
  462.  
  463. /*---------------*/
  464. /* Open the File */
  465. /*---------------*/
  466. inshandle=Open(fname,MODE_OLDFILE);
  467. if(inshandle==0)
  468.   {  kill8svx("Cannot open 8SVX file.\n");  return(1L);  }
  469.  
  470. /*---------------*/
  471. /* Read the File */
  472. /*---------------*/
  473. rd8count=Read(inshandle,iobuffer,8);
  474. if(rd8count==-1){kill8svx ("Read error, 8SVX file.\n");return(1L);}
  475. if(rd8count<8)  {kill8svx ("Not an IFF 8SVX file, too short\n");return(1L);}
  476.  
  477. /*-----------------*/
  478. /* Evaluate Header */
  479. /*-----------------*/
  480. p8Chunk=(Chunk *)iobuffer;
  481. if( p8Chunk->ckID != FORM ) {kill8svx("Not an IFF FORM.\n");return(1L);}
  482. if(p8Chunk->ckSize > 65536) {kill8svx("8SVX file too big\n");return(1L);}
  483.  
  484. /*------------------*/
  485. /* Get the IFF data */
  486. /*------------------*/
  487. p8data=(UBYTE *) AllocMem(fsize=p8Chunk->ckSize , MEMF_PUBLIC|MEMF_CLEAR);
  488. if(p8data==0)    {kill8svx("Memory allocate failed.\n");return(1L);}
  489.  
  490. rd8count=Read(inshandle,p8data,p8Chunk->ckSize);
  491. if(rd8count==-1)
  492.    {kill8svx ("Error during read.\n");return(1L);}
  493. if(rd8count<p8Chunk->ckSize)
  494.    {kill8svx ("Malformed IFF, too short.\n");return(1L);}
  495.  
  496. /*-------------------*/
  497. /* Evaluate IFF Type */
  498. /*-------------------*/
  499. if(MakeID( *p8data, *(p8data+1) , *(p8data+2) , *(p8data+3) ) != I8SVX )
  500.   {kill8svx("Sorry, this is not IFF 8SVX.\n");return(1L);}
  501.  
  502. /*--------------*/
  503. /* Find the IDs */
  504. /*--------------*/
  505.  
  506. data8count=4;
  507.  
  508. while( data8count < fsize )
  509.   {
  510.   p8Chunk=(Chunk *)(p8data+data8count);
  511.  
  512.   switch(p8Chunk->ckID)
  513.     {
  514.     case VHDR:
  515.       pVoice8Header=(Voice8Header *)(p8data+data8count+8L);
  516.  
  517.       ocyc  = pVoice8Header->samplesPerHiCycle;
  518.  
  519.       ifreq = pVoice8Header->samplesPerSec / ocyc;
  520.  
  521.       /*-------------------------*/
  522.       /* Convert given frequency */
  523.       /* to a MIDI note number   */
  524.       /* ifreq = input frequency */
  525.       /* midin = the MIDI note   */
  526.       /* Works only 27.5-12544 Hz*/
  527.       /* MIDI notes 21  - 127    */
  528.       /*-------------------------*/
  529.       y=0;x=1;ifreq*=10;
  530.       while(ifreq > 535*x) {x*=2;y++;}
  531.       freq=275*x;
  532.       freq=(freq*103)/100;
  533.       midin=21+(12*y);
  534.       for(x=0;x<11;x++)
  535.         {
  536.         if(ifreq>freq)
  537.           {
  538.           freq=(freq*106)/100;
  539.           midin++;
  540.           }
  541.         else x=12;
  542.         }
  543.       ifreq=ifreq/10;
  544.  
  545.       ptabptr=(LONG *) AllocMem( 64 , MEMF_PUBLIC|MEMF_CLEAR);
  546.       if(ptabptr==0)  {kill8svx("Memory allocate failed.\n");return(1L);}
  547.       ptabptrs[instrumentno]=ptabptr;
  548.       /*-----------------------------------*/
  549.       /* MIDI octaves go from C to B.      */
  550.       /* Find the octave of the hi sample. */
  551.       /*-----------------------------------*/
  552.       x=120;
  553.       while(midin<x)x=x-12;
  554.       hic=x;
  555.       if (hic> 108) {kill8svx("Sample out of range.");return(1L);}
  556.  
  557.       /*--------------------------------------------*/
  558.       /* Compute ticks per cycle for each midi note */
  559.       /*--------------------------------------------*/
  560.       x=(108-hic)/12;
  561.       y=1; while (x>0) { y*=2; x--; }
  562.       for(x=0; x<12; x++)  ptabptr[x]= ( clock * y) / maxfreq[x] ;
  563.  
  564.       /*-------------------------------*/
  565.       /* Factor in # samples per cycle */
  566.       /*-------------------------------*/
  567.       for(x=0; x<12; x++) ptabptr[x]/=ocyc;
  568.  
  569.       break;
  570.  
  571.  
  572.     case BODY:
  573.       /*--------------------------------*/
  574.       /* Allocate chip memory for all   */
  575.       /* octaves and copy samples to it.*/
  576.       /*--------------------------------*/
  577.       sbase=(UBYTE *)AllocMem( ssize=p8Chunk->ckSize ,
  578.                                MEMF_CHIP | MEMF_CLEAR);
  579.       if(sbase==0)   {kill8svx("Memory allocate failed.\n");return(1L);}
  580.       ssizes[instrumentno]=ssize;
  581.       sbases[instrumentno]=sbase;
  582.  
  583.       tempptr=p8data + data8count + 8L;
  584.       for(x=0;x<ssize;x++) *(sbase+x) = *(tempptr+x);
  585.       /*-------------------------------*/
  586.       /* Now add the pointers to our   */
  587.       /* list of pointers in psample[] */
  588.       /*--------------------------------*/
  589.       if(pVoice8Header->ctOctave > 8)
  590.         {kill8svx("Too many samples.\n");return(1L);}
  591.       y=1;
  592.       for(x=0; x < 8; x++)
  593.         {
  594.         both=pVoice8Header->oneShotHiSamples +
  595.              pVoice8Header->repeatHiSamples;
  596.         both = (both*(y-1)) + (LONG)sbase;
  597.         psample[scount]  =(BYTE *)both;
  598.         psample[scount+1]=(BYTE *)both+(y*pVoice8Header->oneShotHiSamples);
  599.         length[scount  ]=y*pVoice8Header->oneShotHiSamples;
  600.         length[scount+1]=y*pVoice8Header->repeatHiSamples;
  601.         if(length[scount  ]==0) length[scount  ]=length[scount+1];
  602.         if(length[scount+1]==0){length[scount+1]=length[scount  ];
  603.                                psample[scount+1]=psample[scount];}
  604.         scount++;scount++;
  605.         if( (9-x <= hic/12) && (9-x > hic/12-pVoice8Header->ctOctave+1) )
  606.                 y*=2;
  607.         }
  608.       break;
  609.     default:
  610.       break;
  611.     }
  612.     /* end switch */
  613.  
  614.   data8count += 8L + p8Chunk->ckSize;
  615.   if(p8Chunk->ckSize&1L ==1) data8count++;
  616.   }
  617. instrumentno++;
  618. if(inshandle!=0){Close(inshandle);inshandle=0;}
  619. if(p8data !=0)  {FreeMem(p8data,fsize);p8data=0;data8count=0;}
  620. return(0L);
  621.  
  622. }
  623. /*----------------*/
  624. /* Abort the Read */
  625. /*----------------*/
  626. void
  627. kill8svx(kill8svxstring)
  628. char *kill8svxstring;
  629. {
  630.    UBYTE x;
  631.    puts(kill8svxstring);
  632.    if(inshandle!=0)Close(inshandle);
  633.    if(p8data !=0)FreeMem(p8data,fsize);
  634.    for(x=0;x<INLIMIT;x++)
  635.      {
  636.      if(ptabptrs[x]!=0) FreeMem (ptabptrs[x]  , 64);
  637.      if(sbases[x]  !=0) FreeMem (sbases[x], ssizes[x]);
  638.      }
  639. }
  640.  
  641.  
  642.  
  643. -----------------------------MORE CODE STARTS HERE------------------
  644.  
  645. /*----------------*/
  646. /*   INCLUDES     */
  647. /*----------------*/
  648. #include "exec/types.h"
  649. #include "exec/memory.h"
  650. #include "devices/timer.h"
  651. #include "devices/audio.h"
  652. #include "libraries/dos.h"
  653. #include "iff/smus.h"
  654.  
  655. /*----------------*/
  656. /*  SUB ROUTINES  */
  657. /*----------------*/
  658. extern APTR       AllocMem();
  659. struct MsgPort   *CreatePort();
  660. struct Message   *GetMsg();
  661. struct IORequest *CreateExtIO();
  662. struct Task      *FindTask();
  663. BYTE              SetTaskPri();
  664. ULONG             OpenDevice();
  665. ULONG             Wait();
  666.  
  667. void              notes();        /* Play the SMUS events         */
  668. ULONG             compute();      /* Fill in the attack/main IOBs */
  669. void              setime();       /* Set the time out period      */
  670. ULONG             setaudio();     /* Set up for notes()           */
  671. void              killaudio();    /* Finish up notes()            */
  672. void              userkill();     /* Our own break handling       */
  673.  
  674. /*--------------*/
  675. /*    GLOBALS   */
  676. /*--------------*/
  677. extern LONG      *ptabptr,*ptabptrs[];
  678. extern LONG       instrumentno;
  679. extern LONG       length[];
  680. extern LONG       trakcount;
  681. extern BYTE      *psample[];
  682.  
  683. struct IOAudio     *AudioIOBptr[20];/* 2 attack parts, 2 main parts and  */
  684. struct Message     *msg;            /* 1 stop part x 4 channels = 20 IOB */
  685. struct MsgPort     *port[4];        /* 4 ports for attack/main AIOBs     */ 
  686. struct MsgPort     *tport[4];       /* 4 ports for timeouts              */
  687. ULONG               device[4];      /* Treat as 4 seperate audio devices */
  688. struct timerequest *treq[8];        /* 2 x 4 timer request IOBs          */
  689. ULONG               timer[4];       /* Treat as 4 seperate timers        */
  690.  
  691. UBYTE               inreg[4];              /* 4 intrument registers    */
  692. UBYTE               chan1[]  = {  1 };     /* Audio channel allocation */
  693. UBYTE               chan2[]  = {  2 };     /* arrays                   */
  694. UBYTE               chan3[]  = {  4 };
  695. UBYTE               chan4[]  = {  8 };
  696. UBYTE              *chans[] = {chan1,chan2,chan3,chan4};
  697.  
  698.             /*------------------------------------------------------------*/
  699.             /*  Tick Table for SMUS Players by Dan Baker. Use the lower 7 */
  700.             /*  bits of a note SEvent->data field as an index into this   */
  701.             /*  table. Scale to local hardware requirements.              */
  702.             /*  whole / half / quartr / 8th / 16th / 32nd  / 64th / 128th */
  703.             /*  ------ ------- ------ ------ ------ ------- ------ ------ */
  704. ULONG ttable[]={26880,  13440,  6720,  3360,  1680,   840,   420,   210,
  705. /* dotted */    40320,  20160, 10080,  5040,  2520,  1260,   630,   315,
  706. /* 2/3    */    17920,   8960,  4480,  2240,  1120,   560,   280,   140,
  707.                 26880,  13440,  6720,  3360,  1680,   840,   420,   210,
  708. /* 5/6    */    21504,  10752,  5376,  2688,  1344,   672,   336,   168,
  709.                 32256,  16128,  8064,  4032,  2016,  1008,   504,   252,
  710. /* 6/7    */    23040,  11520,  5760,  2880,  1440,   720,   360,   180,
  711.                 34560,  17280,  8640,  4320,  2160,  1080,   540,   270  };
  712.  
  713.  
  714. /*===============================*/
  715. /*             NOTE              */
  716. /*===============================*/
  717. void
  718. notes(trak)
  719. SEvent **trak;
  720. {
  721. /*-------------*/
  722. /* NOTE locals */
  723. /*-------------*/
  724. ULONG        stat,wakebit,waitmask;
  725. ULONG        mbits[8];
  726. UBYTE        x,chn,donecount,odev[4];
  727. BYTE         oldpri;
  728. struct Task *mt;
  729.  
  730. /*--------------------------------*/
  731. /* Set up the Signals, Initialize */
  732. /*--------------------------------*/
  733. if(trakcount>4)trakcount=4;
  734. for(chn=0;chn<4;chn++)inreg[chn]=chn;
  735. if(instrumentno<4)
  736.   {
  737.   for(chn=instrumentno;chn<4;chn++)inreg[chn]=0;
  738.   }
  739.  
  740. /* Time out signals for 4 channels */
  741. mbits[0] = (1 << tport[0]->mp_SigBit);
  742. mbits[1] = (1 << tport[1]->mp_SigBit);
  743. mbits[2] = (1 << tport[2]->mp_SigBit);
  744. mbits[3] = (1 << tport[3]->mp_SigBit);
  745.  
  746. /* Attack-part-done signals        */
  747. mbits[4] = (1 <<  port[0]->mp_SigBit);
  748. mbits[5] = (1 <<  port[1]->mp_SigBit);
  749. mbits[6] = (1 <<  port[2]->mp_SigBit);
  750. mbits[7] = (1 <<  port[3]->mp_SigBit);
  751.  
  752.  
  753. waitmask = mbits[0] | mbits [1] | mbits[2] | mbits [3] |
  754.            mbits[4] | mbits [5] | mbits[6] | mbits [7] | SIGBREAKF_CTRL_C;
  755.  
  756. mt=FindTask(NULL);
  757. oldpri=SetTaskPri(mt,22);              /* Bump our priority a bit. */
  758.  
  759. /*-----------------------------*/
  760. /* Compute the 1st 8 AudioIOBs */
  761. /*-----------------------------*/
  762. for(chn=0;chn<trakcount;chn++)
  763.   {
  764.   odev[chn]=0;
  765.   stat=compute(trak,chn,odev[chn]);
  766.  
  767.   /*------------------------------------*/
  768.   /* Initialize 20 AIOBs.  These fields */
  769.   /* only need to be filled once.       */
  770.   /*------------------------------------*/
  771.   AudioIOBptr[chn]->ioa_Request.io_Command                =CMD_WRITE;
  772.   AudioIOBptr[chn]->ioa_Request.io_Flags                  =ADIOF_PERVOL;
  773.                                                  /* Main part, 1st IO  */
  774.   *AudioIOBptr[chn+4]=*AudioIOBptr[chn];         /* Main part, 2nd IO  */
  775.   *AudioIOBptr[chn+8]=*AudioIOBptr[chn];         /* Attack part, 1st IO */
  776.   *AudioIOBptr[chn+12]=*AudioIOBptr[chn];        /* Attack part, 2nd IO */
  777.  
  778.   *AudioIOBptr[chn+16]=*AudioIOBptr[chn];
  779.   AudioIOBptr[chn+16]->ioa_Request.io_Command              =ADCMD_FINISH;
  780.   AudioIOBptr[chn+16]->ioa_Request.io_Flags                =IOF_QUICK;
  781.   }
  782.  
  783. /*-------------------------*/
  784. /*  Compute second 8 AIOBs */
  785. /*-------------------------*/
  786. for(chn=0;chn<trakcount;chn++)
  787.   {
  788.   odev[chn]=4;
  789.   stat=compute(trak,chn,odev[chn]);
  790.   }
  791. /*---------------------------*/
  792. /* Start the 1st 8 AudioIOBs */
  793. /*---------------------------*/
  794. for(chn=0;chn<trakcount;chn++)
  795.   {
  796.   BeginIO(AudioIOBptr[chn+8]);
  797.   BeginIO(AudioIOBptr[chn]);
  798.   }
  799. for(chn=0;chn<trakcount;chn++)  SendIO(treq[chn]);
  800.  
  801. /*-------------------------------------------*/
  802. /* Main Loop: while there is more trak data, */
  803. /* Wait() for a main note to finish, then    */
  804. /* Begin() the next one.  Compute() successor*/
  805. /* Always has 2 notes, 4 AudioIOBs ready.    */
  806. /*-------------------------------------------*/
  807. donecount=0;
  808. while(donecount<trakcount)
  809.   {
  810.   wakebit=0L;
  811.   while(wakebit==0L)
  812.     {
  813.      /*---------------------------------------*/
  814.      /* Wake up from attack part. Just start  */
  815.      /* the main part playing (by GetMsg) Its */
  816.      /* already in the queue waiting to begin */
  817.      /*---------------------------------------*/
  818.      wakebit=Wait(waitmask);
  819.      for(x=0;x<trakcount;x++)
  820.        {
  821.        if(wakebit&mbits[x+4])
  822.             {
  823.             msg=GetMsg(port[x]);
  824.             wakebit&=~mbits[x+4];
  825.             }
  826.        }
  827.     }
  828.  
  829.   /*-------------------------------------------------------*/
  830.   /* Wake up from a main part time-out.  Now kill both the */
  831.   /* attack and main part of the current note.  Start the  */
  832.   /* attack and main of the next note, and the timer.      */
  833.   /*-------------------------------------------------------*/
  834.   for(x=0;x<trakcount;x++)
  835.     {
  836.     if(wakebit&mbits[x]){chn=x+odev[x];
  837.                          BeginIO(AudioIOBptr[16+x]);
  838.                          BeginIO(AudioIOBptr[16+x]);
  839.                          BeginIO(AudioIOBptr[chn+8]);
  840.                          BeginIO(AudioIOBptr[chn]);
  841.                          SendIO(treq[chn]);}
  842.     }
  843.  
  844.   /*-----------------------------------------------------------*/
  845.   /* Dispose of messages and compute() the successor notes for */
  846.   /* any that finished. As signals bits are processed, they    */
  847.   /* are removed from the wakeup mask (up to 4 bits to handle).*/
  848.   /*-----------------------------------------------------------*/
  849.   while(wakebit!=0)
  850.     {
  851.     if     (wakebit&mbits[0]) {chn=0;wakebit&=~mbits[0];}
  852.     else if(wakebit&mbits[1]) {chn=1;wakebit&=~mbits[1];}
  853.     else if(wakebit&mbits[2]) {chn=2;wakebit&=~mbits[2];}
  854.     else if(wakebit&mbits[3]) {chn=3;wakebit&=~mbits[3];}
  855.  
  856.     else if(wakebit&SIGBREAKF_CTRL_C) {userkill();return(0);}
  857.     else   {puts("Wake up bits unknown!\n");userkill();return(0);}
  858.  
  859.     msg=GetMsg(tport[chn]);
  860.     msg=GetMsg(port[chn]);
  861.  
  862.     if  (odev[chn]==4)odev[chn]=0;     /* odev[] determines which of */
  863.     else              odev[chn]=4;     /* the 2 AudioIOBs to re-use. */
  864.  
  865.     stat=compute(trak,chn,odev[chn]);
  866.     if(stat==1) donecount++;
  867.     /* Done with this wake up bit */
  868.  
  869.     }
  870.   /* All wake up bits done*/
  871.  
  872.   }
  873. /* All traks done */
  874.  
  875. /*--------------------------*/
  876. /* Finish the last 4 notes. */
  877. /*--------------------------*/
  878.  
  879. while( donecount < (2*trakcount) )
  880.   {
  881.   wakebit=Wait(waitmask);
  882.   for(x=0;x<trakcount;x++)
  883.     {
  884.     if(wakebit&mbits[x+4]) msg=GetMsg(port[x]);
  885.     }
  886.  
  887.   for(x=0;x<trakcount;x++)
  888.     {
  889.     if(wakebit&mbits[x]){ BeginIO(AudioIOBptr[16+x]);
  890.                           BeginIO(AudioIOBptr[16+x]);
  891.                           donecount++;}
  892.     }
  893.   }
  894. for(chn=0;chn<trakcount;chn++){ msg=GetMsg( tport[chn]);
  895.                                 msg=GetMsg( port[chn]); }
  896. }
  897.  
  898.  
  899.  
  900. /*----------------------------------*/
  901. /* Compute: calculates SMUS note    */
  902. /* and duration values and fills in */
  903. /* the attack and main AudioIOBs.   */
  904. /*----------------------------------*/
  905. ULONG
  906. compute(track,ch,odev)
  907.  
  908. SEvent          **track;                /* Track pointer          */
  909. UBYTE             ch;                   /* Channel to use (0-4)   */
  910. UBYTE             odev;                 /* AIOB set to use 0 or 4 */
  911.  
  912. {
  913. SEvent            *cevent;               /* Local SEvent          */
  914. UBYTE              psamin;               /* Index into psample    */
  915. UBYTE              aiob;                 /* Local AIOB index      */
  916. UBYTE              note;                 /* MIDI note number      */
  917. UBYTE              dur;                  /* duration in secs      */
  918. struct IOAudio     *AAptr;               /* Local attack AudioIOB */
  919. struct IOAudio     *Aptr;                /* Local main AudioIOB   */
  920. struct timerequest *tr;                  /* Local time request    */
  921. ULONG              micro,sec;            /* Timer variables       */   
  922.  
  923. /*-------------------*/
  924. /* Initialize locals */
  925. /*-------------------*/
  926. aiob  =ch+odev;
  927. AAptr =AudioIOBptr[aiob+8];       /* 8-15 */
  928. Aptr  =AudioIOBptr[aiob];         /* 0-7  */
  929. tr    =treq[aiob];                /* 0-7  */
  930. cevent=track[ch];
  931.  
  932. while(cevent->sID >128 || cevent->data >127)
  933.            {
  934.            /* Handle instrument change */
  935.            if(cevent->sID==129 & cevent->data<instrumentno)
  936.                inreg[ch]=cevent->data;
  937.            /* Ignore other non-note events, 1-trak chords */
  938.            track[ch]++; cevent++;
  939.            /* If we're out of notes, drop out of loop with a rest */
  940.            if(track[ch]>=track[ch+4]) {note=128;goto DONE;}
  941.            }
  942. note  =cevent->sID;
  943.  
  944. /*------------------------------*/
  945. /* Fill in the Audio IOB fields */
  946. /*------------------------------*/
  947. DONE:
  948.        dur   =cevent->data;
  949.            
  950.       /*--------*/
  951.       /* Volume */
  952.       /*--------*/
  953.       Aptr->ioa_Volume=60; dur&=63;
  954.       if(note==128){Aptr->ioa_Volume=0;note=90;}
  955.       AAptr->ioa_Volume=Aptr->ioa_Volume;
  956.  
  957.       /*-------------*/
  958.       /* Data/Length */
  959.       /*-------------*/
  960.       psamin=9;while(note>11){note-=12;psamin--;}
  961.       psamin=(psamin<<1) + (inreg[ch]<<4) ;
  962.  
  963.       Aptr->ioa_Data                    =(UBYTE *)psample[psamin+1];
  964.       Aptr->ioa_Length                  =(length [psamin+1]);
  965.       AAptr->ioa_Data                   =(UBYTE *)psample[psamin];
  966.       AAptr->ioa_Length                 =(length [psamin]);
  967.  
  968.       /*--------*/
  969.       /* Period */
  970.       /*--------*/
  971.       ptabptr          =ptabptrs[inreg[ch]];
  972.       Aptr->ioa_Period =ptabptr[note];
  973.       AAptr->ioa_Period=ptabptr[note];
  974.  
  975.       /*------------*/
  976.       /* Set Timer  */ 
  977.       /*------------*/ 
  978.       sec=0;
  979.       micro=ttable[dur];
  980.       while(micro>1000000){sec++;micro-=1000000;}
  981.       tr->tr_node.io_Command=TR_ADDREQUEST;
  982.       tr->tr_time.tv_secs=sec;
  983.       tr->tr_time.tv_micro=micro;
  984.  
  985.       /*--------*/
  986.       /* Cycles */
  987.       /*--------*/
  988.       Aptr->ioa_Cycles =0;   /* Main part plays forever  (until timeout) */
  989.       AAptr->ioa_Cycles=1;   /* Attack part plays only once              */
  990.  
  991.       track[ch]++;
  992.       if(track[ch] > track[ch+4]) return(1L);
  993.       else                         return(0L);
  994. }
  995.  
  996.  
  997. /*========================*/
  998. /*      KILL  AUDIO       */
  999. /*========================*/
  1000. void
  1001. killaudio()
  1002. {
  1003. UBYTE c;
  1004. for(c=0;c<4;c++)
  1005.   {
  1006.     if(device[c]==0)     CloseDevice(AudioIOBptr[c]);
  1007.  
  1008.     if(timer[c] ==0)     CloseDevice(treq[c]);
  1009.  
  1010.     if( port[c]  !=0)    DeletePort(port[c]);
  1011.     if(tport[c]  !=0)    DeletePort(tport[c]);
  1012.  
  1013.     if(treq[c  ]!=0)     DeleteExtIO(treq[c  ]);
  1014.     if(treq[c+4]!=0)     DeleteExtIO(treq[c+4]);
  1015.  
  1016.     if(AudioIOBptr[c  ]!=0)
  1017.        FreeMem( AudioIOBptr[c  ],sizeof(struct IOAudio) );
  1018.     if(AudioIOBptr[c+4]!=0)
  1019.        FreeMem( AudioIOBptr[c+4],sizeof(struct IOAudio) );
  1020.     if(AudioIOBptr[c+8]!=0)
  1021.        FreeMem( AudioIOBptr[c+8],sizeof(struct IOAudio) );
  1022.     if(AudioIOBptr[c+12]!=0)
  1023.        FreeMem( AudioIOBptr[c+12],sizeof(struct IOAudio) );
  1024.     if(AudioIOBptr[c+16]!=0)
  1025.        FreeMem( AudioIOBptr[c+16],sizeof(struct IOAudio) );
  1026.   }
  1027. }
  1028.  
  1029. /*=========================*/
  1030. /*     SET  UP  AUDIO      */
  1031. /*=========================*/
  1032. ULONG setaudio()
  1033. {
  1034. UBYTE c;
  1035. for(c=0;c<20;c++)AudioIOBptr[c]=0;
  1036. for(c=0;c<4;c++){treq[c]=0;treq[c+4]=0;
  1037.                  port[c]=0;tport[c]=0;
  1038.                  timer[c]=1;device[c]=1;}
  1039. for(c=0;c<20;c++)
  1040.   {
  1041.   /*------------------------------*/
  1042.   /* Allocate audio I/O blocks    */
  1043.   /*------------------------------*/
  1044.   AudioIOBptr[c]=(struct IOAudio *)
  1045.          AllocMem( sizeof(struct IOAudio),MEMF_CHIP|MEMF_PUBLIC|MEMF_CLEAR);
  1046.   if(AudioIOBptr[c]==0) {return(1L);}
  1047.   }
  1048.  
  1049. for(c=0;c<4;c++)
  1050.   {
  1051.   port[c]=CreatePort(0,0);
  1052.   if(port[c]==0) {return(1L);}
  1053.  
  1054.   /*-----------------------------------------------*/
  1055.   /* Set up audio I/O block for channel allocation */
  1056.   /*-----------------------------------------------*/
  1057.   AudioIOBptr[c]->ioa_Request.io_Message.mn_ReplyPort   = port[c];
  1058.   AudioIOBptr[c]->ioa_Request.io_Message.mn_Node.ln_Pri = 21;
  1059.   AudioIOBptr[c]->ioa_AllocKey                          = 0;
  1060.   AudioIOBptr[c]->ioa_Data                              = chans[c];
  1061.   AudioIOBptr[c]->ioa_Length                            = 1;
  1062.  
  1063.   /*------------------------------------------------------------*/
  1064.   /* Open the audio device and allocate once for each channel   */
  1065.   /*------------------------------------------------------------*/
  1066.   device[c]=OpenDevice("audio.device",0,AudioIOBptr[c],0);
  1067.   if(device[c]!=0) {return(1L);}
  1068.   }
  1069.  
  1070. for(c=0;c<4;c++)
  1071.   {
  1072.   tport[c]=CreatePort(0,0);
  1073.   if(tport[c]==0) {return(1L);}
  1074.  
  1075.   treq[c]=(struct timerequest *) CreateExtIO( tport[c] ,
  1076.                                    sizeof(struct timerequest) );
  1077.   if(treq[c]==0)  {return(1L);}
  1078.  
  1079.   treq[c+4]=(struct timerequest *) CreateExtIO( tport[c] ,
  1080.                                    sizeof(struct timerequest) );
  1081.   if(treq[c+4]==0)  {return(1L);}
  1082.  
  1083.   /*---------------------------*/
  1084.   /*  Open the timer device    */
  1085.   /*---------------------------*/
  1086.   timer[c]=OpenDevice(TIMERNAME , UNIT_MICROHZ , treq[c] , 0);
  1087.   if(timer[c]!=0) {return(1L);}
  1088.  
  1089.   *treq[c+4]=*treq[c];
  1090.   }
  1091. return(0L);
  1092. }
  1093.  
  1094. /*=========================*/
  1095. /* Userkill Break Handling */
  1096. /*=========================*/
  1097. void
  1098. userkill()
  1099. {
  1100. UBYTE x;
  1101. /* Kill 4 notes on 4 channels, 4 timer requests */
  1102. for(x=16;x<16+trakcount;x++)BeginIO(AudioIOBptr[x]);
  1103. for(x=16;x<16+trakcount;x++)BeginIO(AudioIOBptr[x]);
  1104. for(x=16;x<16+trakcount;x++)BeginIO(AudioIOBptr[x]);
  1105. for(x=16;x<16+trakcount;x++)BeginIO(AudioIOBptr[x]);
  1106.  
  1107. for(x=0;x<trakcount  ;x++)AbortIO(treq[x]);
  1108. for(x=4;x<4+trakcount;x++)AbortIO(treq[x]);
  1109.  
  1110. for(x=0;x<trakcount;x++){msg=GetMsg(tport[x]);
  1111.                          msg=GetMsg( port[x]);}
  1112. }
  1113.